tboot: use TXT's DMA-protected DMAR table to setup VT-d
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 29 Jan 2009 12:44:31 +0000 (12:44 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 29 Jan 2009 12:44:31 +0000 (12:44 +0000)
Signed-off-by: Joseph Cihula <joseph.cihula@intel.com>
xen/arch/x86/tboot.c
xen/drivers/passthrough/vtd/dmar.c
xen/include/asm-x86/tboot.h

index a6646d5d7c527ba8148658eeac7059b7d465487f..92f509ae957dda2bef791b329f94438732fdff14 100644 (file)
@@ -18,6 +18,10 @@ tboot_shared_t *g_tboot_shared;
 
 static const uuid_t tboot_shared_uuid = TBOOT_SHARED_UUID;
 
+/* used by tboot_protect_mem_regions() and/or tboot_parse_dmar_table() */
+static uint64_t txt_heap_base, txt_heap_size;
+static uint64_t sinit_base, sinit_size;
+
 /*
  * TXT configuration registers (offsets from TXT_{PUB, PRIV}_CONFIG_REGS_BASE)
  */
@@ -37,10 +41,33 @@ static const uuid_t tboot_shared_uuid = TBOOT_SHARED_UUID;
 
 extern char __init_begin[], __per_cpu_start[], __per_cpu_end[], __bss_start[];
 
+#define SHA1_SIZE      20
+typedef uint8_t   sha1_hash_t[SHA1_SIZE];
+
+typedef struct __packed {
+    uint32_t     version;             /* currently 6 */
+    sha1_hash_t  bios_acm_id;
+    uint32_t     edx_senter_flags;
+    uint64_t     mseg_valid;
+    sha1_hash_t  sinit_hash;
+    sha1_hash_t  mle_hash;
+    sha1_hash_t  stm_hash;
+    sha1_hash_t  lcp_policy_hash;
+    uint32_t     lcp_policy_control;
+    uint32_t     rlp_wakeup_addr;
+    uint32_t     reserved;
+    uint32_t     num_mdrs;
+    uint32_t     mdrs_off;
+    uint32_t     num_vtd_dmars;
+    uint32_t     vtd_dmars_off;
+} sinit_mle_data_t;
+
 void __init tboot_probe(void)
 {
     tboot_shared_t *tboot_shared;
     unsigned long p_tboot_shared;
+    uint32_t map_base, map_size;
+    unsigned long map_addr;
 
     /* Look for valid page-aligned address for shared page. */
     p_tboot_shared = simple_strtoul(opt_tboot, NULL, 0);
@@ -68,6 +95,30 @@ void __init tboot_probe(void)
     printk("  shutdown_entry: 0x%08x\n", tboot_shared->shutdown_entry);
     printk("  tboot_base: 0x%08x\n", tboot_shared->tboot_base);
     printk("  tboot_size: 0x%x\n", tboot_shared->tboot_size);
+
+    /* these will be needed by tboot_protect_mem_regions() and/or
+       tboot_parse_dmar_table(), so get them now */
+
+    map_base = PFN_DOWN(TXT_PUB_CONFIG_REGS_BASE);
+    map_size = PFN_UP(NR_TXT_CONFIG_PAGES * PAGE_SIZE);
+    map_addr = (unsigned long)__va(map_base << PAGE_SHIFT);
+    if ( map_pages_to_xen(map_addr, map_base, map_size, __PAGE_HYPERVISOR) )
+        return;
+
+    /* TXT Heap */
+    txt_heap_base =
+        *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_BASE);
+    txt_heap_size =
+        *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_SIZE);
+
+    /* SINIT */
+    sinit_base =
+        *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_BASE);
+    sinit_size =
+        *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_SIZE);
+
+    destroy_xen_mappings((unsigned long)__va(map_base << PAGE_SHIFT),
+                         (unsigned long)__va((map_base + map_size) << PAGE_SHIFT));
 }
 
 void tboot_shutdown(uint32_t shutdown_type)
@@ -125,33 +176,26 @@ int tboot_in_measured_env(void)
 
 int __init tboot_protect_mem_regions(void)
 {
-    uint64_t base, size;
-    uint32_t map_base, map_size;
-    unsigned long map_addr;
     int rc;
 
     if ( !tboot_in_measured_env() )
         return 1;
 
-    map_base = PFN_DOWN(TXT_PUB_CONFIG_REGS_BASE);
-    map_size = PFN_UP(NR_TXT_CONFIG_PAGES * PAGE_SIZE);
-    map_addr = (unsigned long)__va(map_base << PAGE_SHIFT);
-    if ( map_pages_to_xen(map_addr, map_base, map_size, __PAGE_HYPERVISOR) )
-        return 0;
-
     /* TXT Heap */
-    base = *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_BASE);
-    size = *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_SIZE);
+    if ( txt_heap_base == 0 )
+        return 0;
     rc = e820_change_range_type(
-        &e820, base, base + size, E820_RESERVED, E820_UNUSABLE);
+        &e820, txt_heap_base, txt_heap_base + txt_heap_size,
+        E820_RESERVED, E820_UNUSABLE);
     if ( !rc )
         return 0;
 
     /* SINIT */
-    base = *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_BASE);
-    size = *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_SIZE);
+    if ( sinit_base == 0 )
+        return 0;
     rc = e820_change_range_type(
-        &e820, base, base + size, E820_RESERVED, E820_UNUSABLE);
+        &e820, sinit_base, sinit_base + sinit_size,
+        E820_RESERVED, E820_UNUSABLE);
     if ( !rc )
         return 0;
 
@@ -163,11 +207,61 @@ int __init tboot_protect_mem_regions(void)
     if ( !rc )
         return 0;
 
+    return 1;
+}
+
+int __init tboot_parse_dmar_table(acpi_table_handler dmar_handler)
+{
+    uint32_t map_base, map_size;
+    unsigned long map_vaddr;
+    void *heap_ptr;
+    struct acpi_table_header *dmar_table;
+    int rc;
+
+    if ( !tboot_in_measured_env() )
+        return acpi_table_parse(ACPI_SIG_DMAR, dmar_handler);
+
+    /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
+    /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
+
+    if ( txt_heap_base == 0 )
+        return 1;
+
+    /* map TXT heap into Xen addr space */
+    map_base = PFN_DOWN(txt_heap_base);
+    map_size = PFN_UP(txt_heap_size);
+    map_vaddr = (unsigned long)__va(map_base << PAGE_SHIFT);
+    if ( map_pages_to_xen(map_vaddr, map_base, map_size, __PAGE_HYPERVISOR) )
+        return 1;
+
+    /* walk heap to SinitMleData */
+    heap_ptr = __va(txt_heap_base);
+    /* skip BiosData */
+    heap_ptr += *(uint64_t *)heap_ptr;
+    /* skip OsMleData */
+    heap_ptr += *(uint64_t *)heap_ptr;
+    /* skip OsSinitData */
+    heap_ptr += *(uint64_t *)heap_ptr;
+    /* now points to SinitMleDataSize; set to SinitMleData */
+    heap_ptr += sizeof(uint64_t);
+    /* get addr of DMAR table */
+    dmar_table = (struct acpi_table_header *)(heap_ptr +
+            ((sinit_mle_data_t *)heap_ptr)->vtd_dmars_off - sizeof(uint64_t));
+
+    rc = dmar_handler(dmar_table);
+
     destroy_xen_mappings(
         (unsigned long)__va(map_base << PAGE_SHIFT),
         (unsigned long)__va((map_base + map_size) << PAGE_SHIFT));
-
-    return 1;
+  
+    /* acpi_parse_dmar() zaps APCI DMAR signature in TXT heap table */
+    /* but dom0 will read real table, so must zap it there too */
+    dmar_table = NULL;
+    acpi_get_table(ACPI_SIG_DMAR, 0, &dmar_table);
+    if ( dmar_table != NULL )
+        ((struct acpi_table_dmar *)dmar_table)->header.signature[0] = '\0';
+
+    return rc;
 }
 
 /*
index 861ee62d79b0a21f246160cadecd0150b60d474a..d334bea4fdb83bfba75802cc12a25a6ed0492065 100644 (file)
@@ -506,6 +506,15 @@ static int __init acpi_parse_dmar(struct acpi_table_header *table)
     return ret;
 }
 
+#ifdef CONFIG_X86
+#include <asm/tboot.h>
+/* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
+/* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
+#define parse_dmar_table(h) tboot_parse_dmar_table(h)
+#else
+#define parse_dmar_table(h) acpi_table_parse(ACPI_SIG_DMAR, h)
+#endif
+
 int acpi_dmar_init(void)
 {
     int rc;
@@ -519,7 +528,7 @@ int acpi_dmar_init(void)
     if ( !iommu_enabled )
         goto fail;
 
-    rc = acpi_table_parse(ACPI_SIG_DMAR, acpi_parse_dmar);
+    rc = parse_dmar_table(acpi_parse_dmar);
     if ( rc )
         goto fail;
 
index 4aa89c0f946484eb4c33aa858dde0488ec2e82b9..471e11f25cc387791e21c10446919285712682ed 100644 (file)
@@ -37,6 +37,8 @@
 #ifndef __TBOOT_H__
 #define __TBOOT_H__
 
+#include <xen/acpi.h>
+
 #ifndef __packed
 #define __packed   __attribute__ ((packed))
 #endif
@@ -110,6 +112,7 @@ void tboot_probe(void);
 void tboot_shutdown(uint32_t shutdown_type);
 int tboot_in_measured_env(void);
 int tboot_protect_mem_regions(void);
+int tboot_parse_dmar_table(acpi_table_handler dmar_handler);
 
 #endif /* __TBOOT_H__ */